#ifndef __M_EXCHANGE_H__
#define __M_EXCHANGE_H__
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <google/protobuf/map.h>//适配protobuf里args的类型
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

// 1. 定义交换机类
struct Exchange
{
    using ptr = std::shared_ptr<Exchange>;
    // 1.交换机名称
    std::string name;
    // 2.交换机类型
    mymq::ExchangeType type;
    // 3.交换机持久化标志
    bool durable;
    // 4.是否自动删除标志
    bool auto_delete;
    // 5.其他参数
    google::protobuf::Map<std::string, std::string> args;

    Exchange() {}
    Exchange(const std::string &ename,
             mymq::ExchangeType etype,
             bool edurable,
             bool eauto_delete,
             const google::protobuf::Map<std::string, std::string> &eargs) : name(ename), type(etype), durable(edurable), auto_delete(eauto_delete), args(eargs) {}

    // args存储键值对，在往数据库存储时，会组织一个格式字符串进行存储 key=val&key=val...
    void setArgs(const std::string &str_args)
    {
        //"key1=val1&key2=val2&" -> key1:val1 key2:val2 存储到unordered_map中
        std::vector<std::string> sub_args;
        StrHelper::split(str_args, "&", sub_args);
        for (auto &str : sub_args)
        {
            size_t pos = str.find("="); // 把key和val分别提取出来
            std::string key = str.substr(0, pos);
            std::string val = str.substr(pos + 1);
            args[key] = val;
        }
    }
    // 将args中的内容进行序列化后，返回一个字符串
    std::string getArgs()
    {
        // unordered_map中的数据key1:val1 key2:val2 -> "key1=val1&key2=val2&"
        std::string res;
        for (auto start = args.begin(); start != args.end(); ++start)
        {
            res += start->first + "=" + start->second + "&";
        }
        return res;
    }
};

// 2. 定义交换机数据持久化管理类--数据存储在sqlite数据库中
using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;
class ExchangeMapper
{
public:
    ExchangeMapper(const std::string &dbfile) : _sql_helper(dbfile)
    {
        std::string path = FileHelper::parentDirectory(dbfile); // 先创建其父级路径
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        createTable();
    }

    // 创建/删除交换机数据表
    void createTable()
    {
#define CREATE_TABLE "create table if not exists exchange_table(\
            name varchar(32) primary key, \
            type int, \
            durable int, \
            auto_delete int, \
            args varchar(128));"
        bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("创建交换机数据库表失败！");
            abort(); // 直接异常退出程序
        }
    }

    void removeTable()
    {
#define DROP_TABLE "drop table if exists exchange_table;"
        bool ret = _sql_helper.exec(DROP_TABLE, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除交换机数据库表失败！");
            abort();
        }
    }

    // 新增交换机数据
    bool insert(Exchange::ptr &exchange)
    {
        std::stringstream ss; // 也可以使用C的方式：snprintf
        ss << "insert into exchange_table values(";
        ss << "'" << exchange->name << "',";
        ss << exchange->type << ",";
        ss << exchange->durable << ",";
        ss << exchange->auto_delete << ",";
        ss << "'" << exchange->getArgs() << "');";
        return _sql_helper.exec(ss.str(), nullptr, nullptr);
    }

    // 移除交换机数据
    void remove(const std::string &name)
    {
        std::stringstream ss;
        ss << "delete from exchange_table where name=";
        ss << "'" << name << "';";
        _sql_helper.exec(ss.str(), nullptr, nullptr);
    }

    // 恢复交换机数据
    ExchangeMap recovery()
    {
        ExchangeMap res;
        std::string sql = "select name, type, durable, auto_delete, args from exchange_table;";
        _sql_helper.exec(sql, selectCallback, &res);
        return res;
    }

private:
    static int selectCallback(void *arg, int numcol, char **row, char **fields)
    { // static是因为参数中无this指针从而符合callback的类型
        ExchangeMap *res = (ExchangeMap *)arg;
        auto exch = std::make_shared<Exchange>();
        exch->name = row[0];
        exch->type = (mymq::ExchangeType)std::stoi(row[1]);
        exch->durable = (bool)std::stoi(row[2]);
        exch->auto_delete = (bool)std::stoi(row[3]);
        if (row[4])
            exch->setArgs(row[4]);
        res->insert(std::make_pair(exch->name, exch));
        return 0;
    }
    SqliteHelper _sql_helper;
};

// 3. 定义交换机数据内存管理类（对外的接口）
class ExchangeManager
{
public:
    using ptr = std::shared_ptr<ExchangeManager>;
    ExchangeManager(const std::string &dbfile) : _mapper(dbfile)
    {
        _exchanges = _mapper.recovery();
    }

    // 声明交换机
    bool declareExchange(const std::string &name,
                         mymq::ExchangeType type,
                         bool durable,
                         bool auto_delete,
                         const google::protobuf::Map<std::string, std::string> &args)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if (it != _exchanges.end())
        {
            // 如果交换机已经存在，直接返回，不需要重新新增
            return true;
        }
        auto exch = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
        if (durable == true)
        {
            bool ret = _mapper.insert(exch);
            if (ret == false)
                return false;
        }
        _exchanges.insert(std::make_pair(name, exch));
        return true;
    }

    // 删除交换机
    void deleteExchange(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if (it == _exchanges.end())
        {
            return;
        }
        if (it->second->durable == true)
        {
            _mapper.remove(name);
        }
        _exchanges.erase(name);
    }

    // 获取指定交换机对象
    Exchange::ptr selectExchange(const std::string &name)
    {   
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if (it == _exchanges.end())
        {
            return Exchange::ptr();
        }
        return it->second;
    }

    // 判断交换机是否存在
    bool exists(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if (it == _exchanges.end())
        {
            return false;
        }
        return true;
    }
    // 清理所有交换机数据
    void clear()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _exchanges.clear();
    }

    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _exchanges.size();
    }

private:
    std::mutex _mutex; // 有多个线程同时访问的可能，加锁
    ExchangeMapper _mapper;
    ExchangeMap _exchanges;
};

#endif